home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / parsedate.y < prev    next >
Encoding:
Lex Description  |  1996-01-30  |  20.7 KB  |  811 lines

  1. %{
  2. /* $Revision: 1.1.1.1 $
  3. **
  4. **  Originally written by Steven M. Bellovin <smb@research.att.com> while
  5. **  at the University of North Carolina at Chapel Hill.  Later tweaked by
  6. **  a couple of people on Usenet.  Completely overhauled by Rich $alz
  7. **  <rsalz@osf.org> and Jim Berets <jberets@bbn.com> in August, 1990.
  8. **  Further revised (removed obsolete constructs and cleaned up timezone
  9. **  names) in August, 1991, by Rich.  Paul Eggert <eggert@twinsun.com>
  10. **  helped in September, 1992.
  11. **
  12. **  This grammar has six shift/reduce conflicts.
  13. **
  14. **  This code is in the public domain and has no copyright.
  15. */
  16. /* SUPPRESS 530 *//* Empty body for statement */
  17. /* SUPPRESS 593 on yyerrlab *//* Label was not used */
  18. /* SUPPRESS 593 on yynewstate *//* Label was not used */
  19. /* SUPPRESS 595 on yypvt *//* Automatic variable may be used before set */
  20. #include <stdio.h>
  21. #include <sys/types.h>
  22. #include <ctype.h>
  23. #include <time.h>
  24.  
  25. #define yyparse        date_parse
  26. #define yylex        date_lex
  27. #define yyerror        date_error
  28.  
  29.  
  30.     /* See the LeapYears table in Convert. */
  31. #define EPOCH        1970
  32. #define END_OF_TIME    2038
  33.     /* Constants for general time calculations. */
  34. #define DST_OFFSET    1
  35. #define SECSPERDAY    (24L * 60L * 60L)
  36.     /* Readability for TABLE stuff. */
  37. #define HOUR(x)        (x * 60)
  38.  
  39. #define LPAREN        '('
  40. #define RPAREN        ')'
  41. #define IS7BIT(x)    ((unsigned int)(x) < 0200)
  42.  
  43. #define SIZEOF(array)    ((int)(sizeof array / sizeof array[0]))
  44. #define ENDOF(array)    (&array[SIZEOF(array)])
  45.  
  46.  
  47. /*
  48. **  An entry in the lexical lookup table.
  49. */
  50. typedef struct _TABLE {
  51.     char    *name;
  52.     int        type;
  53.     time_t    value;
  54. } TABLE;
  55.  
  56. /*
  57. **  Daylight-savings mode:  on, off, or not yet known.
  58. */
  59. typedef enum _DSTMODE {
  60.     DSTon, DSToff, DSTmaybe
  61. } DSTMODE;
  62.  
  63. /*
  64. **  Meridian:  am, pm, or 24-hour style.
  65. */
  66. typedef enum _MERIDIAN {
  67.     MERam, MERpm, MER24
  68. } MERIDIAN;
  69.  
  70.  
  71. /*
  72. **  Global variables.  We could get rid of most of them by using a yacc
  73. **  union, but this is more efficient.  (This routine predates the
  74. **  yacc %union construct.)
  75. */
  76. static char    *yyInput;
  77. static DSTMODE    yyDSTmode;
  78. static int    yyHaveDate;
  79. static int    yyHaveRel;
  80. static int    yyHaveTime;
  81. static time_t    yyTimezone;
  82. static time_t    yyDay;
  83. static time_t    yyHour;
  84. static time_t    yyMinutes;
  85. static time_t    yyMonth;
  86. static time_t    yySeconds;
  87. static time_t    yyYear;
  88. static MERIDIAN    yyMeridian;
  89. static time_t    yyRelMonth;
  90. static time_t    yyRelSeconds;
  91.  
  92.  
  93. extern struct tm    *localtime();
  94.  
  95. static void        date_error();
  96. %}
  97.  
  98. %union {
  99.     time_t        Number;
  100.     enum _MERIDIAN    Meridian;
  101. }
  102.  
  103. %token    tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
  104. %token    tUNUMBER tZONE
  105.  
  106. %type    <Number>    tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
  107. %type    <Number>    tSNUMBER tUNUMBER tZONE numzone zone
  108. %type    <Meridian>    tMERIDIAN o_merid
  109.  
  110. %%
  111.  
  112. spec    : /* NULL */
  113.     | spec item
  114.     ;
  115.  
  116. item    : time {
  117.         yyHaveTime++;
  118. #ifdef lint
  119.         /* I am compulsive about lint natterings... */
  120.         if (yyHaveTime == -1) {
  121.         YYERROR;
  122.         }
  123. #endif /* lint */
  124.     }
  125.     | time zone {
  126.         yyHaveTime++;
  127.         yyTimezone = $2;
  128.     }
  129.     | date {
  130.         yyHaveDate++;
  131.     }
  132.     | rel {
  133.         yyHaveRel = 1;
  134.     }
  135.     ;
  136.  
  137. time    : tUNUMBER o_merid {
  138.         if ($1 < 100) {
  139.         yyHour = $1;
  140.         yyMinutes = 0;
  141.         }
  142.         else {
  143.         yyHour = $1 / 100;
  144.         yyMinutes = $1 % 100;
  145.         }
  146.         yySeconds = 0;
  147.         yyMeridian = $2;
  148.     }
  149.     | tUNUMBER ':' tUNUMBER o_merid {
  150.         yyHour = $1;
  151.         yyMinutes = $3;
  152.         yySeconds = 0;
  153.         yyMeridian = $4;
  154.     }
  155.     | tUNUMBER ':' tUNUMBER numzone {
  156.         yyHour = $1;
  157.         yyMinutes = $3;
  158.         yyTimezone = $4;
  159.         yyMeridian = MER24;
  160.         yyDSTmode = DSToff;
  161.     }
  162.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
  163.         yyHour = $1;
  164.         yyMinutes = $3;
  165.         yySeconds = $5;
  166.         yyMeridian = $6;
  167.     }
  168.     | tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
  169.         yyHour = $1;
  170.         yyMinutes = $3;
  171.         yySeconds = $5;
  172.         yyTimezone = $6;
  173.         yyMeridian = MER24;
  174.         yyDSTmode = DSToff;
  175.     }
  176.     ;
  177.  
  178. zone    : tZONE {
  179.         $$ = $1;
  180.         yyDSTmode = DSToff;
  181.     }
  182.     | tDAYZONE {
  183.         $$ = $1;
  184.         yyDSTmode = DSTon;
  185.     }
  186.     | tZONE numzone {
  187.         /* Only allow "GMT+300" and "GMT-0800" */
  188.         if ($1 != 0) {
  189.         YYABORT;
  190.         }
  191.         $$ = $2;
  192.         yyDSTmode = DSToff;
  193.     }
  194.     | numzone {
  195.         $$ = $1;
  196.         yyDSTmode = DSToff;
  197.     }
  198.     ;
  199.  
  200. numzone    : tSNUMBER {
  201.         int        i;
  202.  
  203.         /* Unix and GMT and numeric timezones -- a little confusing. */
  204.         if ($1 < 0) {
  205.         /* Don't work with negative modulus. */
  206.         $1 = -$1;
  207.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  208.             YYABORT;
  209.         }
  210.         $$ = ($1 / 100) * 60 + i;
  211.         }
  212.         else {
  213.         if ($1 > 9999 || (i = $1 % 100) >= 60) {
  214.             YYABORT;
  215.         }
  216.         $$ = -(($1 / 100) * 60 + i);
  217.         }
  218.     }
  219.     ;
  220.  
  221. date    : tUNUMBER '/' tUNUMBER {
  222.         yyMonth = $1;
  223.         yyDay = $3;
  224.     }
  225.     | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
  226.         if ($1 > 100) {
  227.         yyYear = $1;
  228.         yyMonth = $3;
  229.         yyDay = $5;
  230.         }
  231.         else {
  232.         yyMonth = $1;
  233.         yyDay = $3;
  234.         yyYear = $5;
  235.         }
  236.     }
  237.     | tMONTH tUNUMBER {
  238.         yyMonth = $1;
  239.         yyDay = $2;
  240.     }
  241.     | tMONTH tUNUMBER ',' tUNUMBER {
  242.         yyMonth = $1;
  243.         yyDay = $2;
  244.         yyYear = $4;
  245.     }
  246.     | tUNUMBER tMONTH {
  247.         yyDay = $1;
  248.         yyMonth = $2;
  249.     }
  250.     | tUNUMBER tMONTH tUNUMBER {
  251.         yyDay = $1;
  252.         yyMonth = $2;
  253.         yyYear = $3;
  254.     }
  255.     | tDAY ',' tUNUMBER tMONTH tUNUMBER {
  256.         yyDay = $3;
  257.         yyMonth = $4;
  258.         yyYear = $5;
  259.     }
  260.     ;
  261.  
  262. rel    : tSNUMBER tSEC_UNIT {
  263.         yyRelSeconds += $1 * $2;
  264.     }
  265.     | tUNUMBER tSEC_UNIT {
  266.         yyRelSeconds += $1 * $2;
  267.     }
  268.     | tSNUMBER tMONTH_UNIT {
  269.         yyRelMonth += $1 * $2;
  270.     }
  271.     | tUNUMBER tMONTH_UNIT {
  272.         yyRelMonth += $1 * $2;
  273.     }
  274.     ;
  275.  
  276. o_merid    : /* NULL */ {
  277.         $$ = MER24;
  278.     }
  279.     | tMERIDIAN {
  280.         $$ = $1;
  281.     }
  282.     ;
  283.  
  284. %%
  285.  
  286. /* Month and day table. */
  287. static TABLE    MonthDayTable[] = {
  288.     { "january",    tMONTH,  1 },
  289.     { "february",    tMONTH,  2 },
  290.     { "march",        tMONTH,  3 },
  291.     { "april",        tMONTH,  4 },
  292.     { "may",        tMONTH,  5 },
  293.     { "june",        tMONTH,  6 },
  294.     { "july",        tMONTH,  7 },
  295.     { "august",        tMONTH,  8 },
  296.     { "september",    tMONTH,  9 },
  297.     { "october",    tMONTH, 10 },
  298.     { "november",    tMONTH, 11 },
  299.     { "december",    tMONTH, 12 },
  300.     /* The value of the day isn't used... */
  301.     { "sunday",        tDAY, 0 },
  302.     { "monday",        tDAY, 0 },
  303.     { "tuesday",    tDAY, 0 },
  304.     { "wednesday",    tDAY, 0 },
  305.     { "thursday",    tDAY, 0 },
  306.     { "friday",        tDAY, 0 },
  307.     { "saturday",    tDAY, 0 },
  308. };
  309.  
  310. /* Time units table. */
  311. static TABLE    UnitsTable[] = {
  312.     { "year",        tMONTH_UNIT,    12 },
  313.     { "month",        tMONTH_UNIT,    1 },
  314.     { "week",        tSEC_UNIT,    7L * 24 * 60 * 60 },
  315.     { "day",        tSEC_UNIT,    1L * 24 * 60 * 60 },
  316.     { "hour",        tSEC_UNIT,    60 * 60 },
  317.     { "minute",        tSEC_UNIT,    60 },
  318.     { "min",        tSEC_UNIT,    60 },
  319.     { "second",        tSEC_UNIT,    1 },
  320.     { "sec",        tSEC_UNIT,    1 },
  321. };
  322.  
  323. /* Timezone table. */
  324. static TABLE    TimezoneTable[] = {
  325.     { "gmt",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  326.     { "ut",    tZONE,     HOUR( 0) },    /* Universal */
  327.     { "utc",    tZONE,     HOUR( 0) },    /* Universal Coordinated */
  328.     { "cut",    tZONE,     HOUR( 0) },    /* Coordinated Universal */
  329.     { "z",    tZONE,     HOUR( 0) },    /* Greenwich Mean */
  330.     { "wet",    tZONE,     HOUR( 0) },    /* Western European */
  331.     { "bst",    tDAYZONE,  HOUR( 0) },    /* British Summer */
  332.     { "nst",    tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
  333.     { "ndt",    tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
  334.     { "ast",    tZONE,     HOUR( 4) },    /* Atlantic Standard */
  335.     { "adt",    tDAYZONE,  HOUR( 4) },    /* Atlantic Daylight */
  336.     { "est",    tZONE,     HOUR( 5) },    /* Eastern Standard */
  337.     { "edt",    tDAYZONE,  HOUR( 5) },    /* Eastern Daylight */
  338.     { "cst",    tZONE,     HOUR( 6) },    /* Central Standard */
  339.     { "cdt",    tDAYZONE,  HOUR( 6) },    /* Central Daylight */
  340.     { "mst",    tZONE,     HOUR( 7) },    /* Mountain Standard */
  341.     { "mdt",    tDAYZONE,  HOUR( 7) },    /* Mountain Daylight */
  342.     { "pst",    tZONE,     HOUR( 8) },    /* Pacific Standard */
  343.     { "pdt",    tDAYZONE,  HOUR( 8) },    /* Pacific Daylight */
  344.     { "yst",    tZONE,     HOUR( 9) },    /* Yukon Standard */
  345.     { "ydt",    tDAYZONE,  HOUR( 9) },    /* Yukon Daylight */
  346.     { "akst",    tZONE,     HOUR( 9) },    /* Alaska Standard */
  347.     { "akdt",    tDAYZONE,  HOUR( 9) },    /* Alaska Daylight */
  348.     { "hst",    tZONE,     HOUR(10) },    /* Hawaii Standard */
  349.     { "hast",    tZONE,     HOUR(10) },    /* Hawaii-Aleutian Standard */
  350.     { "hadt",    tDAYZONE,  HOUR(10) },    /* Hawaii-Aleutian Daylight */
  351.     { "ces",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  352.     { "cest",    tDAYZONE,  -HOUR(1) },    /* Central European Summer */
  353.     { "mez",    tZONE,     -HOUR(1) },    /* Middle European */
  354.     { "mezt",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  355.     { "cet",    tZONE,     -HOUR(1) },    /* Central European */
  356.     { "met",    tZONE,     -HOUR(1) },    /* Middle European */
  357.     { "eet",    tZONE,     -HOUR(2) },    /* Eastern Europe */
  358.     { "msk",    tZONE,     -HOUR(3) },    /* Moscow Winter */
  359.     { "msd",    tDAYZONE,  -HOUR(3) },    /* Moscow Summer */
  360.     { "wast",    tZONE,     -HOUR(8) },    /* West Australian Standard */
  361.     { "wadt",    tDAYZONE,  -HOUR(8) },    /* West Australian Daylight */
  362.     { "hkt",    tZONE,     -HOUR(8) },    /* Hong Kong */
  363.     { "cct",    tZONE,     -HOUR(8) },    /* China Coast */
  364.     { "jst",    tZONE,     -HOUR(9) },    /* Japan Standard */
  365.     { "kst",    tZONE,     -HOUR(9) },    /* Korean Standard */
  366.     { "kdt",    tZONE,     -HOUR(9) },    /* Korean Daylight */
  367.     { "cast",    tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
  368.     { "cadt",    tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
  369.     { "east",    tZONE,     -HOUR(10) },    /* Eastern Australian Standard */
  370.     { "eadt",    tDAYZONE,  -HOUR(10) },    /* Eastern Australian Daylight */
  371.     { "nzst",    tZONE,     -HOUR(12) },    /* New Zealand Standard */
  372.     { "nzdt",    tDAYZONE,  -HOUR(12) },    /* New Zealand Daylight */
  373.  
  374.     /* For completeness we include the following entries. */
  375. #if 0
  376.  
  377.     /* Duplicate names.  Either they conflict with a zone listed above
  378.      * (which is either more likely to be seen or just been in circulation
  379.      * longer), or they conflict with another zone in this section and
  380.      * we could not reasonably choose one over the other. */
  381.     { "fst",    tZONE,     HOUR( 2) },    /* Fernando De Noronha Standard */
  382.     { "fdt",    tDAYZONE,  HOUR( 2) },    /* Fernando De Noronha Daylight */
  383.     { "bst",    tZONE,     HOUR( 3) },    /* Brazil Standard */
  384.     { "est",    tZONE,     HOUR( 3) },    /* Eastern Standard (Brazil) */
  385.     { "edt",    tDAYZONE,  HOUR( 3) },    /* Eastern Daylight (Brazil) */
  386.     { "wst",    tZONE,     HOUR( 4) },    /* Western Standard (Brazil) */
  387.     { "wdt",    tDAYZONE,  HOUR( 4) },    /* Western Daylight (Brazil) */
  388.     { "cst",    tZONE,     HOUR( 5) },    /* Chile Standard */
  389.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Chile Daylight */
  390.     { "ast",    tZONE,     HOUR( 5) },    /* Acre Standard */
  391.     { "adt",    tDAYZONE,  HOUR( 5) },    /* Acre Daylight */
  392.     { "cst",    tZONE,     HOUR( 5) },    /* Cuba Standard */
  393.     { "cdt",    tDAYZONE,  HOUR( 5) },    /* Cuba Daylight */
  394.     { "est",    tZONE,     HOUR( 6) },    /* Easter Island Standard */
  395.     { "edt",    tDAYZONE,  HOUR( 6) },    /* Easter Island Daylight */
  396.     { "sst",    tZONE,     HOUR(11) },    /* Samoa Standard */
  397.     { "ist",    tZONE,     -HOUR(2) },    /* Israel Standard */
  398.     { "idt",    tDAYZONE,  -HOUR(2) },    /* Israel Daylight */
  399.     { "idt",    tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
  400.     { "ist",    tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
  401.     { "cst",     tZONE,     -HOUR(8) },    /* China Standard */
  402.     { "cdt",     tDAYZONE,  -HOUR(8) },    /* China Daylight */
  403.     { "sst",     tZONE,     -HOUR(8) },    /* Singapore Standard */
  404.  
  405.     /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
  406.     { "gst",    tZONE,     HOUR( 3) },    /* Greenland Standard */
  407.     { "wat",    tZONE,     -HOUR(1) },    /* West Africa */
  408.     { "at",    tZONE,     HOUR( 2) },    /* Azores */
  409.     { "gst",    tZONE,     -HOUR(10) },    /* Guam Standard */
  410.     { "nft",    tZONE,     HOUR(3)+30 }, /* Newfoundland */
  411.     { "idlw",    tZONE,     HOUR(12) },    /* International Date Line West */
  412.     { "mewt",    tZONE,     -HOUR(1) },    /* Middle European Winter */
  413.     { "mest",    tDAYZONE,  -HOUR(1) },    /* Middle European Summer */
  414.     { "swt",    tZONE,     -HOUR(1) },    /* Swedish Winter */
  415.     { "sst",    tDAYZONE,  -HOUR(1) },    /* Swedish Summer */
  416.     { "fwt",    tZONE,     -HOUR(1) },    /* French Winter */
  417.     { "fst",    tDAYZONE,  -HOUR(1) },    /* French Summer */
  418.     { "bt",    tZONE,     -HOUR(3) },    /* Baghdad */
  419.     { "it",    tZONE,     -(HOUR(3)+30) }, /* Iran */
  420.     { "zp4",    tZONE,     -HOUR(4) },    /* USSR Zone 3 */
  421.     { "zp5",    tZONE,     -HOUR(5) },    /* USSR Zone 4 */
  422.     { "ist",    tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
  423.     { "zp6",    tZONE,     -HOUR(6) },    /* USSR Zone 5 */
  424.     { "nst",    tZONE,     -HOUR(7) },    /* North Sumatra */
  425.     { "sst",    tZONE,     -HOUR(7) },    /* South Sumatra */
  426.     { "jt",    tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
  427.     { "nzt",    tZONE,     -HOUR(12) },    /* New Zealand */
  428.     { "idle",    tZONE,     -HOUR(12) },    /* International Date Line East */
  429.     { "cat",    tZONE,     HOUR(10) },    /* -- expired 1967 */
  430.     { "nt",    tZONE,     HOUR(11) },    /* -- expired 1967 */
  431.     { "ahst",    tZONE,     HOUR(10) },    /* -- expired 1983 */
  432.     { "hdt",    tDAYZONE,  HOUR(10) },    /* -- expired 1986 */
  433. #endif /* 0 */
  434. };
  435.  
  436.  
  437. /* ARGSUSED */
  438. static void
  439. date_error(s)
  440.     char    *s;
  441. {
  442.     /* NOTREACHED */
  443. }
  444.  
  445.  
  446. static time_t
  447. ToSeconds(Hours, Minutes, Seconds, Meridian)
  448.     time_t    Hours;
  449.     time_t    Minutes;
  450.     time_t    Seconds;
  451.     MERIDIAN    Meridian;
  452. {
  453.     if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
  454.     return -1;
  455.     if (Meridian == MER24) {
  456.     if (Hours < 0 || Hours > 23)
  457.         return -1;
  458.     }
  459.     else {
  460.     if (Hours < 1 || Hours > 12)
  461.         return -1;
  462.     if (Hours == 12)
  463.         Hours = 0;
  464.     if (Meridian == MERpm)
  465.         Hours += 12;
  466.     }
  467.     return (Hours * 60L + Minutes) * 60L + Seconds;
  468. }
  469.  
  470.  
  471. static time_t
  472. Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, dst)
  473.     time_t    Month;
  474.     time_t    Day;
  475.     time_t    Year;
  476.     time_t    Hours;
  477.     time_t    Minutes;
  478.     time_t    Seconds;
  479.     MERIDIAN    Meridian;
  480.     DSTMODE    dst;
  481. {
  482.     static int    DaysNormal[13] = {
  483.     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  484.     };
  485.     static int    DaysLeap[13] = {
  486.     0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  487.     };
  488.     static int    LeapYears[] = {
  489.     1972, 1976, 1980, 1984, 1988, 1992, 1996,
  490.     2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
  491.     };
  492.     register int    *yp;
  493.     register int    *mp;
  494.     register time_t    Julian;
  495.     register int    i;
  496.     time_t        tod;
  497.  
  498.     if (Year < 0)
  499.     Year = -Year;
  500.     if (Year < 100)
  501.     Year += 1900;
  502.     if (Year < EPOCH)
  503.     Year += 100;
  504.     for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
  505.     if (Year == *yp) {
  506.         mp = DaysLeap;
  507.         break;
  508.     }
  509.     if (Year < EPOCH || Year > END_OF_TIME
  510.      || Month < 1 || Month > 12
  511.      /* NOSTRICT *//* conversion from long may lose accuracy */
  512.      || Day < 1 || Day > mp[(int)Month])
  513.     return -1;
  514.  
  515.     Julian = Day - 1 + (Year - EPOCH) * 365;
  516.     for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++)
  517.     if (Year <= *yp)
  518.         break;
  519.     for (i = 1; i < Month; i++)
  520.     Julian += *++mp;
  521.     Julian *= SECSPERDAY;
  522.     Julian += yyTimezone * 60L;
  523.     if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
  524.     return -1;
  525.     Julian += tod;
  526.     tod = Julian;
  527.     if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
  528.     Julian -= DST_OFFSET * 60L * 60L;
  529.     return Julian;
  530. }
  531.  
  532.  
  533. static time_t
  534. DSTcorrect(Start, Future)
  535.     time_t    Start;
  536.     time_t    Future;
  537. {
  538.     time_t    StartDay;
  539.     time_t    FutureDay;
  540.  
  541.     StartDay = (localtime(&Start)->tm_hour + 1) % 24;
  542.     FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
  543.     return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60L * 60L;
  544. }
  545.  
  546.  
  547. static time_t
  548. RelativeMonth(Start, RelMonth)
  549.     time_t    Start;
  550.     time_t    RelMonth;
  551. {
  552.     struct tm    *tm;
  553.     time_t    Month;
  554.     time_t    Year;
  555.  
  556.     tm = localtime(&Start);
  557.     Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
  558.     Year = Month / 12;
  559.     Month = Month % 12 + 1;
  560.     return DSTcorrect(Start,
  561.         Convert(Month, (time_t)tm->tm_mday, Year,
  562.         (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
  563.         MER24, DSTmaybe));
  564. }
  565.  
  566.  
  567. static int
  568. LookupWord(buff, length)
  569.     char        *buff;
  570.     register int    length;
  571. {
  572.     register char    *p;
  573.     register char    *q;
  574.     register TABLE    *tp;
  575.     register int    c;
  576.  
  577.     p = buff;
  578.     c = p[0];
  579.  
  580.     /* See if we have an abbreviation for a month. */
  581.     if (length == 3 || (length == 4 && p[3] == '.'))
  582.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
  583.         q = tp->name;
  584.         if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
  585.         yylval.Number = tp->value;
  586.         return tp->type;
  587.         }
  588.     }
  589.     else
  590.     for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++)
  591.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  592.         yylval.Number = tp->value;
  593.         return tp->type;
  594.         }
  595.  
  596.     /* Try for a timezone. */
  597.     for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  598.     if (c == tp->name[0] && p[1] == tp->name[1]
  599.      && strcmp(p, tp->name) == 0) {
  600.         yylval.Number = tp->value;
  601.         return tp->type;
  602.     }
  603.  
  604.     /* Try the units table. */
  605.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  606.     if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  607.         yylval.Number = tp->value;
  608.         return tp->type;
  609.     }
  610.  
  611.     /* Strip off any plural and try the units table again. */
  612.     if (--length > 0 && p[length] == 's') {
  613.     p[length] = '\0';
  614.     for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++)
  615.         if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
  616.         p[length] = 's';
  617.         yylval.Number = tp->value;
  618.         return tp->type;
  619.         }
  620.     p[length] = 's';
  621.     }
  622.     length++;
  623.  
  624.     /* Drop out any periods. */
  625.     for (p = buff, q = (char*)buff; *q; q++)
  626.     if (*q != '.')
  627.         *p++ = *q;
  628.     *p = '\0';
  629.  
  630.     /* Try the meridians. */
  631.     if (buff[1] == 'm' && buff[2] == '\0') {
  632.     if (buff[0] == 'a') {
  633.         yylval.Meridian = MERam;
  634.         return tMERIDIAN;
  635.     }
  636.     if (buff[0] == 'p') {
  637.         yylval.Meridian = MERpm;
  638.         return tMERIDIAN;
  639.     }
  640.     }
  641.  
  642.     /* If we saw any periods, try the timezones again. */
  643.     if (p - buff != length) {
  644.     c = buff[0];
  645.     for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++)
  646.         if (c == tp->name[0] && p[1] == tp->name[1]
  647.         && strcmp(p, tp->name) == 0) {
  648.         yylval.Number = tp->value;
  649.         return tp->type;
  650.         }
  651.     }
  652.  
  653.     /* Unknown word -- assume GMT timezone. */
  654.     yylval.Number = 0;
  655.     return tZONE;
  656. }
  657.  
  658.  
  659. int
  660. date_lex()
  661. {
  662.     register char    c;
  663.     register char    *p;
  664.     char        buff[20];
  665.     register int    sign;
  666.     register int    i;
  667.     register int    nesting;
  668.  
  669.     for ( ; ; ) {
  670.     /* Get first character after the whitespace. */
  671.     for ( ; ; ) {
  672.         while (isspace(*yyInput))
  673.         yyInput++;
  674.         c = *yyInput;
  675.  
  676.         /* Ignore RFC 822 comments, typically time zone names. */
  677.         if (c != LPAREN)
  678.         break;
  679.         for (nesting = 1; (c = *++yyInput) != RPAREN || --nesting; )
  680.         if (c == LPAREN)
  681.             nesting++;
  682.         else if (!IS7BIT(c) || c == '\0' || c == '\r'
  683.              || (c == '\\' && ((c = *++yyInput) == '\0' || !IS7BIT(c))))
  684.             /* Lexical error: bad comment. */
  685.             return '?';
  686.         yyInput++;
  687.     }
  688.  
  689.     /* A number? */
  690.     if (isdigit(c) || c == '-' || c == '+') {
  691.         if (c == '-' || c == '+') {
  692.         sign = c == '-' ? -1 : 1;
  693.         yyInput++;
  694.         if (!isdigit(*yyInput))
  695.             /* Skip the plus or minus sign. */
  696.             continue;
  697.         }
  698.         else
  699.         sign = 0;
  700.         for (i = 0; (c = *yyInput++) != '\0' && isdigit(c); )
  701.         i = 10 * i + c - '0';
  702.         yyInput--;
  703.         yylval.Number = sign < 0 ? -i : i;
  704.         return sign ? tSNUMBER : tUNUMBER;
  705.     }
  706.  
  707.     /* A word? */
  708.     if (isalpha(c)) {
  709.         for (p = buff; (c = *yyInput++) == '.' || isalpha(c); )
  710.         if (p < &buff[sizeof buff - 1])
  711.             *p++ = isupper(c) ? tolower(c) : c;
  712.         *p = '\0';
  713.         yyInput--;
  714.         return LookupWord(buff, p - buff);
  715.     }
  716.  
  717.     return *yyInput++;
  718.     }
  719. }
  720.  
  721.  
  722. time_t
  723. parsedate(p)
  724.     char        *p;
  725. {
  726.     extern int        date_parse();
  727.     time_t        Start;
  728.  
  729.     yyInput = p;
  730.  
  731.     yyYear = 0;
  732.     yyMonth = 0;
  733.     yyDay = 0;
  734.     yyTimezone = 0;
  735.     yyDSTmode = DSTmaybe;
  736.     yyHour = 0;
  737.     yyMinutes = 0;
  738.     yySeconds = 0;
  739.     yyMeridian = MER24;
  740.     yyRelSeconds = 0;
  741.     yyRelMonth = 0;
  742.     yyHaveDate = 0;
  743.     yyHaveRel = 0;
  744.     yyHaveTime = 0;
  745.  
  746.     if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
  747.     return -1;
  748.  
  749.     if (yyHaveDate || yyHaveTime) {
  750.     Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
  751.             yyMeridian, yyDSTmode);
  752.     if (Start < 0)
  753.         return -1;
  754.     }
  755.     else
  756.     return -1;
  757.  
  758.     Start += yyRelSeconds;
  759.     if (yyRelMonth)
  760.     Start += RelativeMonth(Start, yyRelMonth);
  761.  
  762.     /* Have to do *something* with a legitimate -1 so it's distinguishable
  763.      * from the error return value.  (Alternately could set errno on error.) */
  764.     return Start == -1 ? 0 : Start;
  765. }
  766.  
  767.  
  768. #ifdef TEST
  769.  
  770. #if YYDEBUG
  771. extern int    yydebug;
  772. #endif /* YYDEBUG */
  773.  
  774. /* ARGSUSED */
  775. int
  776. main(ac, av)
  777.     int        ac;
  778.     char    *av[];
  779. {
  780.     char    buff[128];
  781.     time_t    d;
  782.  
  783. #if YYDEBUG
  784.     yydebug = 1;
  785. #endif /* YYDEBUG */
  786.  
  787.     (void)printf("Enter date, or blank line to exit.\n\t> ");
  788.     for ( ; ; ) {
  789.     (void)printf("\t> ");
  790.     (void)fflush(stdout);
  791.     if (gets(buff) == NULL || buff[0] == '\n')
  792.         break;
  793. #if YYDEBUG
  794.     if (strcmp(buff, "yydebug") == 0) {
  795.         yydebug = !yydebug;
  796.         printf("yydebug = %s\n", yydebug ? "on" : "off");
  797.         continue;
  798.     }
  799. #endif /* YYDEBUG */
  800.     d = parsedate(buff);
  801.     if (d == -1)
  802.         (void)printf("Bad format - couldn't convert.\n");
  803.     else
  804.         (void)printf("%s", ctime(&d));
  805.     }
  806.  
  807.     exit(0);
  808.     /* NOTREACHED */
  809. }
  810. #endif /* TEST */
  811.